package com.example.lotterysystem.service.Impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.dto.WinningRecordDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;


@Slf4j
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private final Long WINNING_RECORDS_TIMEOUT=60*60*24*2L;
    private final String WINNING_RECORDS_PREFIX="WINNING_RECORDS_";
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
//        MessageWrapper wrapper = new MessageWrapper();
//        wrapper.setMessageId(UUID.randomUUID().toString());
//        wrapper.setMessageData(param);
//        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, wrapper);
//
////        log.info("mq准备消息发送：map={}", JacksonUtil.writeValueAsString(map));
////        // 发消息: 交换机、绑定的key、消息体
////        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
//        log.info("mq消息发送成功：map={}", JacksonUtil.writeValueAsString(wrapper));

        Map<String, String> map = new HashMap<>();
        map.put("messageId", UUID.randomUUID().toString());
        map.put("messageData", JacksonUtil.writeValueAsString(param));

        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        log.info("mq消息发送成功：map={}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrize(DrawPrizeParam param) {
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //奖品是否可以从activity_prize中查找，保存activityDO时做了本地事物，保证一致性
        ActivityPrizeDO activityPrizeDO= activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        //活动或奖品是否存在
        if (null==activityDO||null==activityPrizeDO){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            log.info("校验抽奖请求失败！原因:{}",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            return false;
        }
        //活动是否有效
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            log.info("校验抽奖请求失败！原因:{}",ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
        }
        //奖品是否有效
        if (activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            log.info("校验抽奖请求失败！原因:{}",ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
        }
        //中奖者人数是否和设置奖品数合适
        if (activityPrizeDO.getPrizeAmount()!=param.getWinnerList().size()){
            //throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            log.info("校验抽奖请求失败！原因:{}",ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关信息：活动、人员、奖品。。。
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());

        List<UserDO> userDOList= userMapper.batchSelectByIds(
                param.getWinnerList().stream().map(
                        DrawPrizeParam.Winner::getUserId).collect(Collectors.toList()));
        PrizeDO prizeDO = prizeMapper.selecById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        //构造中奖者记录，保存
        List<WinningRecordDO> winnerRecordDOList=userDOList.stream().map(userDO -> {
            WinningRecordDO winnerRecordDO = new WinningRecordDO();
            winnerRecordDO.setActivityId(activityDO.getId());
            winnerRecordDO.setActivityName(activityDO.getActivityName());
            winnerRecordDO.setPrizeId(prizeDO.getId());
            winnerRecordDO.setPrizeName(prizeDO.getName());
            winnerRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
            winnerRecordDO.setWinnerId(userDO.getId());
            winnerRecordDO.setWinnerName(userDO.getUserName());
            winnerRecordDO.setWinnerEmail(userDO.getEmail());
            winnerRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
            winnerRecordDO.setWinningTime(param.getWinningTime());
            return winnerRecordDO;
        }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winnerRecordDOList);
        //缓存中奖者
        //1、缓存奖品维度中奖记录
        cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId(),winnerRecordDOList,WINNING_RECORDS_TIMEOUT);
        //2、缓存活动维度中奖记录
        if (activityDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            //查询活动维度的全量中奖记录
            List<WinningRecordDO> allList= winningRecordMapper.selectByActivityId(param.getActivityId());

            cacheWinningRecords(String.valueOf(param.getActivityId()),allList,WINNING_RECORDS_TIMEOUT);
        }
        return winnerRecordDOList;

    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if (null==activityId||null==prizeId){
            log.warn("要删除中奖记录相关的活动ID为空！");
            return;
        }
        //删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);
        //删除缓存（奖品维度、活动维度）
        if (null!=prizeId){
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //无论是否传递prizeId都要删除活动维度中奖记录缓存
        //如果传递prize ID，证明奖品未被抽奖，必须删除活动维度缓存
        //如果没传递prize ID，就只删除活动维度的信息
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //查询redis

        String key=null==param.getActivityId()
                ?String.valueOf(param.getActivityId())
                : param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecordDO> winnerRecordDOList = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winnerRecordDOList)){
            return convertToWinnerRecordDOList(winnerRecordDOList);
        }
        //如果redis不存在，查库
        winnerRecordDOList= winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());
        //存放记录到redis
        if (CollectionUtils.isEmpty(winnerRecordDOList)){
            log.info("查询的中奖记录为空！param:{}",JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key,winnerRecordDOList,WINNING_RECORDS_TIMEOUT);
        return convertToWinnerRecordDOList(winnerRecordDOList);
    }

    private List<WinningRecordDTO> convertToWinnerRecordDOList(List<WinningRecordDO> winnerRecordDOList) {
        if (CollectionUtils.isEmpty(winnerRecordDOList)){
            return Arrays.asList();
        }
        return winnerRecordDOList.stream().map(winnerRecordDO -> {
            WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
            winningRecordDTO.setWinnerId(winnerRecordDO.getWinnerId());
            winningRecordDTO.setWinnerName(winnerRecordDO.getWinnerName());
            winningRecordDTO.setPrizeName(winnerRecordDO.getPrizeName());
            winningRecordDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winnerRecordDO.getPrizeTier()));
            winningRecordDTO.setWinningTime(winnerRecordDO.getWinningTime());
            return winningRecordDTO;

        }).collect(Collectors.toList());
    }

    /**
     * 从缓存中删除中奖记录
     */
    private void deleteWinningRecords(String key) {
        try {
            if (redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                //删除
                redisUtil.del(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            log.error("删除中奖缓存异常，:{}",key);
        }
    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winnerRecordDOList
     * @param winningRecordsTimeout
     */
    private void cacheWinningRecords(String key, List<WinningRecordDO> winnerRecordDOList, Long winningRecordsTimeout) {
        String str="";
        try {
            if (!StringUtils.hasText(key)|| CollectionUtils.isEmpty(winnerRecordDOList)){
                log.warn("要缓存的内容为空！key:{},value:{}",key,JacksonUtil.writeValueAsString(winnerRecordDOList));
                return;
            }
             str=JacksonUtil.writeValueAsString(winnerRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX+key,str,winningRecordsTimeout);
        }catch (Exception e){
            log.error("保存中奖记录异常！key:{},value:{}",WINNING_RECORDS_PREFIX+key,str);
        }
    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key){
        try {
            if (!StringUtils.hasText(key)){
                log.warn("要查询的中奖记录为空");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(str)){
                return Arrays.asList();
            }
            List<WinningRecordDO> winnerRecordDOList = JacksonUtil.readListValue(str, WinningRecordDO.class);
            return winnerRecordDOList;
        }catch (Exception e){
            log.error("从缓存中查询中奖记录异常！key:{}",WINNING_RECORDS_PREFIX+key);
            return Arrays.asList();
        }
    }
}